#include"StdAfx.h"


pgEventManager *pgPaleGemFramework::GetFrameworkEventManager()
{
	// Returning a null pointer to force initialisation
	if (!m_pFrameworkEventManager)
	{
		MessageBox(NULL, L"Trying to acces PaleGem framework event manager before it has been created!\n This usually means pgInit() hasn't been called yet!", L"PaleGem Error", MB_OK);
		return NULL;
	}
	else
	{
		return m_pFrameworkEventManager;
	}
}

CLogfile *pgPaleGemFramework::GetLogfile()
{
	if (!m_pLogfile)
	{
		MessageBox(NULL, L"Trying to acces PaleGem Logfile before it has been created!\n This usually means pgInit() hasn't been called yet!", L"PaleGem Error", MB_OK);
		return NULL;
	}
	else
	{
		return m_pLogfile;
	}
}

pgRender *pgPaleGemFramework::GetRenderer()
{
	if(!m_pRenderer)
	{
		MessageBox(NULL, L"Trying to acces PaleGem Renderer before it has been created!\n This usually means pgInit() hasn't been called yet!", L"PaleGem Error", MB_OK);
		return NULL;
	}
	else
	{
		return m_pRenderer;
	}
}

pgTimer *pgPaleGemFramework::GetTimer()
{
	if (!m_pTimer)
	{
		MessageBox(NULL, L"Trying to acces PaleGem Timer before it has been created!\n This usually means pgInit() hasn't been called yet!", L"PaleGem Error", MB_OK);
		return NULL;
	}
	else
	{
		return m_pTimer;
	}
}

pgInputEventManager *pgPaleGemFramework::GetInputEventManager()
{
	if (!m_pInputEventManager)
	{
		MessageBox(NULL, L"Trying to acces PaleGem input event manager before it has been created!\n This usually means pgInit() hasn't been called yet!", L"PaleGem Error", MB_OK);
		return NULL;
	}
	else
	{
		return m_pInputEventManager;
	}
}

pgMouseInputManager	*pgPaleGemFramework::GetMouseInputManager()
{
	if (!m_pInputEventManager)
	{
		MessageBox(NULL, L"Trying to acces PaleGem mouse input manager before it has been created!\n This usually means pgInit() hasn't been called yet!", L"PaleGem Error", MB_OK);
		return NULL;
	}
	else
	{
		return m_pMouseInputManager;
	}
}


HRESULT pgPaleGemFramework::pgInit(HINSTANCE hInstance, int cmdShow, LPCWSTR lpGameTitle)
{
	m_pFrameworkEventManager = new pgEventManager();
	m_pLogfile = new CLogfile();

	g_PG_pLog->CreateLogfile("PaleGem_Log.html");

	PG_LOG_ALWAYS_EX(TOPIC_PGGAME_APP, "PaleGem is initializing...");
	
	g_PG_pSGameInfo->Validate();

#ifdef _DEBUG
	int tmpDebugFlag = _CrtSetDbgFlag(_CRTDBG_REPORT_FLAG);
	tmpDebugFlag |= _CRTDBG_LEAK_CHECK_DF;
	_CrtSetDbgFlag(tmpDebugFlag);
#endif

	//---------------------------------------------------------------------------------------------------
	PreInit();
	//---------------------------------------------------------------------------------------------------
	m_pTimer = new pgTimer();
	g_PG_pTimer->Init();
	//---------------------------------------------------------------------------------------------------
	if (!g_PG_pSGameInfo->GetWindowHandle())
		CreateGameWindow(hInstance, cmdShow);			// Create the game window
	//---------------------------------------------------------------------------------------------------
	m_pRenderer = new pgRender();

	if(!g_PG_pRenderer->Init())
		return false;
	//---------------------------------------------------------------------------------------------------
	m_pInputEventManager = new pgInputEventManager();

	m_pMouseInputManager = new pgMouseInputManager();

	g_PG_pFrameworkEventManager->RegisterEvent(0, 0, 0);

	pgVec3 vec1(8,9,10);
	pgVec3 vec2(0.0f,1.0f,0.0f);

	vec2 += vec1;


	PG_LOG_INFO_EX(TOPIC_PGGAME_APP, "vec1 len: %f", vec1.Length());

	vec1 = vec1.Normalize();

	PG_LOG_INFO_EX(TOPIC_PGGAME_APP, "vec1 len: %f", vec1.Length());
	
	return true;
}

 HRESULT pgPaleGemFramework::pgExit()
{
	m_pMouseInputManager->ShutDown();
	PG_SAFE_DELETE(m_pMouseInputManager);

	PG_SAFE_DELETE(m_pInputEventManager);
	//-------------------------------------------------------------
	//-------------------------------------------------------------
	g_PG_pRenderer->Release();
	PG_SAFE_DELETE(m_pRenderer);
	//-------------------------------------------------------------
	
	//-------------------------------------------------------------
	PG_LOG_INFO_EX(TOPIC_PGGAME_APP, "Average FPS: %f!", g_PG_pTimer->GetAverageFPS());
	PG_LOG_INFO_EX(TOPIC_PGGAME_APP, "Last FPS: %f!", 1.0f / g_PG_pTimer->GetTicks());
	PG_SAFE_DELETE(m_pTimer);
	//-------------------------------------------------------------
	g_PG_pSGameInfo->Del();

	PG_SAFE_DELETE(m_pLogfile);

	g_PG_pFrameworkEventManager->ShutDown();
	PG_SAFE_DELETE(m_pFrameworkEventManager);

	IObject::ClearAll();
	return true;
}

HRESULT pgPaleGemFramework::CreateGameWindow(HINSTANCE hInstance, int cmdShow)
{
	// Create out a windowclass
	WNDCLASSEX windowClassEx;
	windowClassEx.cbSize = sizeof(WNDCLASSEX);
	windowClassEx.style = CS_HREDRAW | CS_VREDRAW;
    windowClassEx.lpfnWndProc = PGWindowProcedure;
    windowClassEx.cbClsExtra = 0;
    windowClassEx.cbWndExtra = 0;
    windowClassEx.hInstance = hInstance;
    windowClassEx.hIcon = NULL;
    windowClassEx.hCursor = LoadCursor( NULL, IDC_ARROW );
    windowClassEx.hbrBackground = ( HBRUSH )( COLOR_WINDOW + 1 );
    windowClassEx.lpszMenuName = NULL;
	windowClassEx.lpszClassName = g_PG_pSGameInfo->GetGameTitle();
    windowClassEx.hIconSm = NULL;

	HRESULT hrCreationSuccess;
	if (FAILED(hrCreationSuccess = RegisterClassEx(&windowClassEx)))
	{
		PG_LOG_ERROR("Failed to register the game window!", hrCreationSuccess, __FILE__, __LINE__);
		return hrCreationSuccess;
	}

	PG_LOG_INFO_EX(TOPIC_PGGAME_APP, "Succeded in registering windowclass");

	// Create the actual window

	RECT windowRect = { 0, 0, g_PG_pSGameInfo->GetScreenWidth(), g_PG_pSGameInfo->GetScreenHeight()};
	AdjustWindowRect(&windowRect, WS_OVERLAPPEDWINDOW, FALSE);
	g_PG_pSGameInfo->SetWindowHandle(CreateWindow(g_PG_pSGameInfo->GetGameTitle(), g_PG_pSGameInfo->GetGameTitle(),
									WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, CW_USEDEFAULT,		//Style, x, 
									windowRect.right - windowRect.left, windowRect.bottom - windowRect.top, //width, height
									NULL, NULL, hInstance, NULL)); //parentWindow, Menu, Instance, lPara

	if(!g_PG_pSGameInfo->GetWindowHandle())
	{
		PG_LOG_ERROR("Failed to create game window!", -1, __FILE__, __LINE__);
		return E_FAIL;
	}

	ShowWindow(g_PG_pSGameInfo->GetWindowHandle(), cmdShow);

	PG_LOG_INFO_EX(TOPIC_PGGAME_APP, "Successfully created game window");
	return S_OK;
}

LRESULT CALLBACK PGWindowProcedure(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
	PAINTSTRUCT ps;
    HDC hdc;
	switch(message)
	{
	case WM_PAINT:
		{
            hdc = BeginPaint(hWnd, &ps);
            EndPaint(hWnd, &ps);
		}break;

    case WM_DESTROY:
		{
            PostQuitMessage(0);
		}break;

	case WM_KEYDOWN:
		{
			
		}break;
	default:
		return DefWindowProc(hWnd, message, wParam, lParam);
	}

	return 0;
}

void pgPaleGemFramework::pgUpdate()
{
	m_pFrameworkEventManager->RegisterEvent(EVENT_NAME::E_UNKNOWN, EVENT_CLASS::EC_FRAMEWORK, new SEventData_E_UNKNOWN(g_PG_pTimer->GetTicks()));
	m_pTimer->Update();
	m_pFrameworkEventManager->Update(0.0f);
	m_pRenderer->Render();
}

void pgPaleGemFramework::pgDoMessageLoop()
{
	MSG msg = {0};
    while(WM_QUIT != msg.message)
    {
        if(PeekMessage(&msg, NULL, 0, 0, PM_REMOVE))
        {
            TranslateMessage(&msg);
            DispatchMessage(&msg);
        }
		else
		{
			pgUpdate();
		}
    }
}

void pgPaleGemFramework::pgDoMessageLoop(int (*UpdateFunction) (float fFrameTime))
{
	MSG msg = {0};
    while(WM_QUIT != msg.message)
    {
        if(PeekMessage(&msg, NULL, 0, 0, PM_REMOVE))
        {
            TranslateMessage(&msg);
            DispatchMessage(&msg);
        }
		else
		{
			UpdateFunction(m_pTimer->GetTicks());
			pgUpdate();
		}
    }
}

void pgPaleGemFramework::OnEvent(IEvent *eEvent)
{
	PG_LOG_INFO_EX(TOPIC_PGGAME_APP, "On Event!");

	
	if(eEvent->eventId == E_ONINIT && eEvent->eventClass == EC_UNKNOWN)
		PG_LOG_INFO_EX(TOPIC_PGGAME_APP, "HIT!");
}

PALEGEM_API void ClearAllObjects()
{
	//IObject::ClearAll();
}

void pgPaleGemFramework::PreInit()
{
	pgCheckHardDiskSpace(PGSPEC_NEEDED_HARDDISK_SPACE);
}

bool pgPaleGemFramework::pgCheckForMultipleInstances(LPCWSTR lpGameTitle)
{
	if (g_PG_pSGameInfo->IsDevmode() == true)
		return false;

	HANDLE handle = CreateMutex(NULL, TRUE, lpGameTitle);

	if (GetLastError() != ERROR_SUCCESS)
	{
		HWND hwnd = FindWindow(lpGameTitle, NULL);

		if(hwnd)
		{
			return true;
		}
	}
	return false;
}

bool pgPaleGemFramework::pgCheckHardDiskSpace(const DWORDLONG spaceNeeded)
{
	// TODO
	// Weird avail memory
	int const drive = _getdrive();
	struct _diskfree_t diskfree;

	_getdiskfree(drive, &diskfree);

	///*unsigned*/ _int64 const neededClusters = spaceNeeded / (diskfree.sectors_per_cluster * diskfree.bytes_per_sector);

	g_PG_pSGameInfo->llAvailableDiskspace = diskfree.sectors_per_cluster * diskfree.bytes_per_sector * diskfree.avail_clusters;
	g_PG_pLog->fTextoutTopic(GREEN, TOPIC_PGGAME_APP,"Available DiskSpace: %d Bytes, Bytes per sector %d, sectors per cluster %d, clusters %d \n", g_PG_pSGameInfo->llAvailableDiskspace, diskfree.bytes_per_sector, (int)diskfree.sectors_per_cluster, diskfree.avail_clusters);


	//if (diskfree.avail_clusters < neededClusters)
	//{
	//	PG_ERROR(ER_NOT_ENOUGH_HARD_DISK_SPACE);
	//	return false;
	//}
	  
	return true;
}

bool pgPaleGemFramework::pgCheckRAM(const DWORDLONG spaceNeeded)
{
	MEMORYSTATUSEX status;
	status.dwLength = sizeof(status);
	bool succes = GlobalMemoryStatusEx(&status);

	
	if (succes)
	{
		g_PG_pSGameInfo->dwlTotalPhysRam = status.ullTotalPhys;
	

		if (status.ullTotalPhys < (spaceNeeded))
		{
			PG_ERROR(ER_NOT_ENOUGH_PHYS_RAM);
			return false;
		}
	}
	else
	{
		g_PG_pSGameInfo->dwlTotalPhysRam = 0;
		PG_LOG_ERROR_SIMPLE("Failed to detect the total amount of physical RAM!", -1);
	}
	return true;
}

bool pgPaleGemFramework::pgCheckAvailRAM(const DWORDLONG spaceNeeded)
{
	/*MEMORYSTATUSEX status;
	GlobalMemoryStatusEx(&status);

	if (status.ullAvailVirtual < spaceNeeded)
	{
		PG_ERROR(ER_NOT_ENOUGH_VIRT_RAM);
		return false;
	} */
	return true;
}

void pgPaleGemFramework::HandleException(PG_ERROR error)
{
	g_PG_pFrameworkEventManager->RegisterEvent(E_ONCRITICAL_ERROR, EC_FRAMEWORK, NULL);
	switch (error)
	{
	case ER_NOT_ENOUGH_PHYS_RAM:
		{
			PG_LOG_ERROR_SIMPLE("Not enough physical RAM installed!", error);
			MessageBox(NULL, L"Not enough physical RAM installed! \n Consider upgrading your machine! \n Aborting initialisation!", L"PaleGem Error", MB_OK);
		}break;
	case ER_D3D_INIT_FAILURE:
		{
			PG_LOG_ERROR_SIMPLE("Failed to initialise Direct3D! See rest of log for more info!", error);
			MessageBox(NULL, L"Failed to initialise renderer! \n See log for more info! \n Aborting initialisation!", L"PaleGem Error", MB_OK);
		}break;
	default:
		{
			PG_LOG_ERROR_SIMPLE("Unhandeled exception occured! Shutting down!", error);
		}	
	}

	g_PG_pPaleGemFramework->pgExit();
	g_PG_pPaleGemFramework->Del();
	
	exit(-1);
}

void pgPaleGemFramework::HandleExceptionCustom(const char* formattedMessage, int returnCode, ...)
{
	g_PG_pFrameworkEventManager->RegisterEvent(E_ONCRITICAL_ERROR, EC_FRAMEWORK, NULL);
	g_PG_pFrameworkEventManager->Update(0.0f);

	char buffer[1024];
	va_list pArgList;

	va_start (pArgList, formattedMessage);
	vsprintf (buffer, formattedMessage, pArgList);
	va_end (pArgList);

	PG_LOG_ERROR_SIMPLE(buffer, returnCode);
	MessageBox(NULL, LPCWSTR(buffer), L"PaleGem Error", MB_OK);
	g_PG_pPaleGemFramework->pgExit();
}

bool SGameInfo::SetInfo(int nwidth, int nheight, 
				bool bwindowed, bool bdevmode, 
				LPCWSTR lpgametitle, HWND hWindow) 
{
	Reset();
	if( nwidth != 0 && nheight != 0)
	{
		width = nwidth;
		height = nheight;
		bWindowed = bwindowed;
		bDevmode = bdevmode;
		lpGameTitle = lpgametitle;
		bIsStillDefaults = false;
		hWindowhandle = hWindow;
		return true;
	}

	return false;
}
bool SGameInfo::Validate()
{
	if(!bIsStillDefaults)
	{
		return true;
	}
	else 
	{
		PG_LOG_WARNING_EX( TOPIC_PGGAME_APP,"GameInfo Structure was not filled out! Using defaults!");
		Reset();
		return false;
	}
}

void SGameInfo::Reset()
{
	width = 1024;
	height = 768;

	bWindowed = true;
	bDevmode = false;
	lpGameTitle = L"PaleGem Game";
	GetModuleFileName(NULL, exePath, MAX_PATH);
	hWindowhandle = NULL;

	bIsStillDefaults = true;   
}